fn main() {
    {
        // This is a Korean name. No problem, because a &str is UTF-8.
        let _name = "서태지";
        // Ț and ș are no problem in UTF-8.
        let _other_name = String::from("Adrian Fahrenheit Țepeș");
    }
    {
        let name = "😂";
        println!("My name is actually {}", name);
    }

    println!();

    {
        // Rust has two main types of strings: String and &str. What is the
        // difference?
        //
        // - &str is a simple string. When you write let my_variable =
        //   "Hello, world!", you create a &str. A &str is very fast.
        // - String is a more complicated string. It is a bit slower, but it has
        //   more functions. A String is a pointer, with data on the heap.
        //
        // Also note that &str has the & in front of it because you need a
        // reference to use a str. That's because of the reason we saw above:
        // the stack needs to know the size. So we give it a & that it knows the
        // size of, and then it is happy.
        //
        // Also, because you use a & to interact with a str, you don't own it.
        // But a String is an owned type. We will soon learn why that is
        // important to know.
        //
        // Let's look at the reason for using a & for strs again to make sure we
        // understand.
        //
        // str is a dynamically sized type (dynamically sized = the size can be
        // different). For example, the names "서태지" and "Adrian Fahrenheit Țepeș"
        // are not the same size.

        // std::mem::size_of::<Type>() gives you the size in bytes of a type.
        println!(
            "A String is always {:?} bytes. It is Sized.",
            std::mem::size_of::<String>()
        );
        println!(
            "And an i8 is always {:?} bytes. It is Sized.",
            std::mem::size_of::<i8>()
        );
        println!(
            "And an f64 is always {:?} bytes. It is Sized.",
            std::mem::size_of::<f64>()
        );
        // std::mem::size_of_val() gives you the size in bytes of a variable.
        println!(
            "But a &str? It can be anything. '서태지' is {:?} bytes. It is not Sized.",
            std::mem::size_of_val("서태지")
        );
        println!(
            "And 'Adrian Fahrenheit Țepeș' is {:?} bytes. It is not Sized.",
            std::mem::size_of_val("Adrian Fahrenheit Țepeș")
        );

        // That is why we need a &, because & makes a pointer, and Rust knows
        // the size of the pointer. So the pointer goes on the stack. If we
        // wrote str, Rust wouldn't know what to do because it doesn't know the
        // size.
    }
    {
        let my_name = "Billybrobby";
        let my_country = "USA";
        let my_home = "Korea";

        let _together = format!(
            "I am {} and I come from {} but I live in {}.",
            my_name, my_country, my_home
        );

        let _my_string: String = "Try to make this a String".into();
    }
}
